#include "tcpclient.h"
#include "ui_tcpclient.h"
#include<QHostInfo>
#include<QMessageBox>
#include<QDateTime>
#include<QProcess>
#include<QDataStream>
#include<QScrollBar>
#include<QFont>
#include<QDebug>
#include <utility>

TcpClient::TcpClient(QWidget *parent) :
    QDialog(parent),
    tcpClient(nullptr),
    localFile(nullptr),
    ui(new Ui::TcpClient)
{
    ui->setupUi(this);

    TotalBytes = 0;                             // 总共需要发送的数据大小
    bytesReceived = 0;                          // 已经接收数据大小
    fileNameSize = 0;                           // 文件名大小
    tcpClient = new QTcpSocket(this);     // 通信套接字
    tcpPort = 6666;                             // 端口号

    // 当有新的数据可读时，发出readyRead()信号
    connect(tcpClient,SIGNAL(readyRead()),
            this,SLOT(readMessage()));
    // 当连接成功时，发出connected()信号
    connect(tcpClient,SIGNAL(error(QAbstractSocket::SocketError)),
            this,SLOT(displayError(QAbstractSocket::SocketError)));

}

TcpClient::~TcpClient()
{
    delete ui;
}

// 设置要传送的文件名
void TcpClient::setFileName(const QString& fileName)
{
    localFile = new QFile(fileName);
}

// 设置服务器地址
void TcpClient::setHostAddress(QHostAddress address)
{
    hostAddress = std::move(address);
    newConnect();
}

// 读取文件
void TcpClient::newConnect()
{
    // 初始化已接收字节为0
    blockSize = 0;
    // 取消已有的连接
    tcpClient->abort();
    // 与服务器连接成功时，发出connected()信号
    tcpClient->connectToHost(hostAddress,tcpPort);
    // 开始计时
    time.start();
}

void TcpClient::readMessage()
{
    // 用于暂存接收的文件
    QDataStream in(tcpClient);
    // 设置数据流版本，这里要和服务器端相同
    in.setVersion(QDataStream::Qt_4_0);

    const float useTime = time.elapsed();

    // 如果接收到的数据小于16个字节，保存到来的文件头结构
    if(bytesReceived <= sizeof(qint64)*2)
    {
        // 接收数据总大小信息和文件名大小信息
        if((tcpClient->bytesAvailable() >= sizeof(qint64)*2) && (fileNameSize ==0))
        {
            // 接收数据总大小信息和文件名大小信息
            in >> TotalBytes >> fileNameSize;
            // 接收文件名，并建立文件
            bytesReceived += sizeof(qint64)*2;
        }
        // 接收文件名，并建立文件
        if((tcpClient->bytesAvailable() >= fileNameSize) && (fileNameSize!=0))
        {
            // 接收文件名，并建立文件
            in >> fileName;
            // 接收完文件名后，总大小减去文件名大小，剩余为文件数据大小
            bytesReceived += fileNameSize;
            // 打开文件
            if(! localFile->open(QFile::WriteOnly))
            {
                // 打开文件失败则返回
                QMessageBox::warning(this,tr("应用程序"),tr("无法读取文件 %1:\n%2.")
                                     .arg(fileName)
                                     .arg(localFile->errorString()));
                return ;
            }
            else return;
        }
    }
    // 如果接收的数据小于总数据，那么写入文件
    if(bytesReceived < TotalBytes)
    {
        // 接收数据，并写入文件
        bytesReceived += tcpClient->bytesAvailable();
        // 读取缓冲区的所有数据
        inBlock = tcpClient->readAll();
        // 将缓冲区数据写入文件
        localFile->write(inBlock);
        // 清空缓冲区
        inBlock.resize(0);
    }

    ui->progressBar->setMaximum(TotalBytes);
    ui->progressBar->setValue(bytesReceived);

    // 更新进度条
    const double speed = bytesReceived / useTime;
    ui->tcpClientStatusLabel->setText(tr("已接收 %1MB( %2MB/s)"
                                         "\n共%3MB 已用时:%4秒\n估计剩余时间:%5秒")
                                      .arg(bytesReceived / (1024*1024))
                                      .arg(speed *1000/(1024*1024),0,'f',2)
                                      .arg(TotalBytes / (1024*1024))
                                      .arg(useTime/1000,0,'f',0)
                                      .arg(TotalBytes/speed/1000 - useTime/1000,0,'f',0 ));

    // 接收数据完成时 关闭文件和套接字 重置变量使界面部件恢复原状并显示接收成功
    if(bytesReceived == TotalBytes)
    {
       localFile ->close();
       tcpClient->close();
       ui->tcpClientStatusLabel->setText(tr("接收文件: %1完毕").arg(fileName));
    }
}

void TcpClient::displayError(QAbstractSocket::SocketError sockError)
{
    // 服务器连接失败时，发出error()信号
    switch (sockError) {
        // 连接被对方关闭
        case QAbstractSocket::RemoteHostClosedError:
            break;
        default:
            qDebug() << tcpClient->errorString();
    }
}

void TcpClient::on_tcpClientCancleBtn_clicked()
{
    // 取消已有的连接
    tcpClient->abort();
    // 关闭文件
    if(localFile->isOpen())
    {
        // 关闭文件
        localFile->close();
    }
    close();
    ui->~TcpClient();
}

void TcpClient::on_tcpClientCloseBtn_clicked()
{
   on_tcpClientCancleBtn_clicked();
}

void TcpClient::closeEvent(QCloseEvent *)
{
    on_tcpClientCloseBtn_clicked();
}